﻿/*
 * This file is part of MonoStrategy.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://monostrategy.codeplex.com/
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Diagnostics;
using MonoStrategy.RenderSystem;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization;

namespace MonoStrategy
{
    public class GUIBindings
    {
        public XMLGUILayout Layout { get; private set; }
        public RootControl RootElement { get; private set; }
        public Control MainMenu { get; private set; }
        public Control ObjectInspector { get; private set; }
        public ListBox ToolConfigList { get; private set; }
        public ListBox StockQuantityList { get; private set; }
        public ListBox StockDistList { get; private set; }
        public ListBox SettlerProfessionList { get; private set; }
        public ListBox SettlerStatList { get; private set; }
        public Label GameTime { get; private set; }

        public GUIBindings(XMLGUILayout inLayout)
        {
            Layout = inLayout;
            RootElement = Layout.RootElement;

            MainMenu = FindControl<Control>("#wizard:MainMenu");
            ObjectInspector = FindControl<Control>("#wizard:ObjectInspector");
            ToolConfigList = FindControl<ListBox>("#list:ToolConfig");
            StockQuantityList = FindControl<ListBox>("#list:StockQuantities");
            StockDistList = FindControl<ListBox>("#list:StockDist");
            SettlerProfessionList = FindControl<ListBox>("#list:SettlerProfessions");
            SettlerStatList = FindControl<ListBox>("#list:SettlerStats");
            GameTime = FindControl<Label>("#label:Time");

            InitializeSettlerConfig();

            Update();
        }

        public void Update()
        {
            Program.Map.UpdateConfig();

            // compute game time
            GameTime.Text = TimeSpan.FromMilliseconds(Program.Map.CurrentCycle * CyclePoint.CYCLE_MILLIS).ToString().Split('.')[0];

            UpdateStockQuantities();
            UpdateSettlerStatistics();
            UpdateObjectInspector();
            UpdateToolConfig();
            UpdateStockDist();
        }

        public void UpdateStockQuantities()
        {
            StockQuantityList.Clear();

            foreach (var resVal in Enum.GetValues(typeof(Resource)))
            {
                Resource res = (Resource)resVal;

                if (res == Resource.Max)
                    continue;

                StockQuantityList.AddItem(new ListBoxItem(res.ToString(), Program.Map.Config.StockQuantities[res].ToString()));
            }
        }

        private void UpdateSettlerStatistics()
        {
            SettlerStatList.Clear();

            foreach (var val in Enum.GetValues(typeof(SettlerStatisticTypes)))
            {
                SettlerStatisticTypes type = (SettlerStatisticTypes)val;

                if (type == SettlerStatisticTypes.Max)
                    continue;

                SettlerStatList.AddItem(new ListBoxItem(type.ToString()));

                (SettlerStatList.FindControl("Text_" + type) as Label).Text = Program.Map.Config.SettlerTypeCounts[type].ToString();
            }

            FindControl<Label>("#label:HouseSpaceCount").Text = Program.Map.Config.HouseSpaceCount.ToString();
            FindControl<Label>("#label:SettlerCount").Text = Program.Map.Config.SettlerCount.ToString();
            FindControl<Label>("#label:SoldierSettlerCount").Text = (Program.Map.Config.SettlerCount + Program.Map.Config.SoldierCount).ToString();
            FindControl<Label>("#label:SoldierCount").Text = Program.Map.Config.SoldierCount.ToString();
        }

        private void InitializeSettlerConfig()
        {
            foreach (var val in Enum.GetValues(typeof(SettlerProfessions)))
            {
                SettlerProfessions prof = (SettlerProfessions)val;

                if (prof == SettlerProfessions.Max)
                    continue;

                SettlerProfessionList.AddItem(new ListBoxItem(prof.ToString()));

                (SettlerProfessionList.FindControl("Up_" + prof) as Button).OnClick += (sender) => { Program.Map.ChangeProfession(prof, 1); };
                (SettlerProfessionList.FindControl("PageUp_" + prof) as Button).OnClick += (sender) => { Program.Map.ChangeProfession(prof, 5); };
                (SettlerProfessionList.FindControl("Down_" + prof) as Button).OnClick += (sender) => { Program.Map.ChangeProfession(prof, -1); };
                (SettlerProfessionList.FindControl("PageDown_" + prof) as Button).OnClick += (sender) => { Program.Map.ChangeProfession(prof, -5); };
            }
        }

        private void UpdateStockDist()
        {
            StockDistList.Clear();

            for(int i = 0; i < Program.Map.Config.StockDistributions.Length; i++)
            {
                var dist = Program.Map.Config.StockDistributions[i];
                var queries = dist.Building.ResourceStacks.Where(e => e.Direction == ResStackType.Query).ToArray();
                var suffix = dist.Building.AnimationClass;

                StockDistList.AddItem(new ListBoxItem(
                    suffix,
                    queries[0].Type.ToString(),
                    queries[1].Type.ToString(),
                    queries[2].Type.ToString()));

                foreach (var forQuery in queries)
                {
                    var query = forQuery; // localization for lambda expression
                    var btn = (StockDistList.FindControl("Btn_" + query.Type + "_" + suffix) as Button);

                    btn.IsDown = !dist.Queries[query.Type];
                    btn.OnClick += (sender) =>
                    {
                        Program.Map.ChangeDistributionSetting(dist.Building.AnimationClass, query.Type, !sender.IsDown);
                    };
                }
            }
        }

        private void UpdateToolConfig()
        {
            Resource[] tools = new Resource[] { 
                Resource.Axe, Resource.Hammer, Resource.Hook, Resource.PickAxe, Resource.Saw, Resource.Scythe, Resource.Shovel, 
                Resource.Bow, Resource.Sword, Resource.Spear,
            };

            ToolConfigList.Clear();

            foreach (var forRes in tools)
            {
                var res = forRes; // localize for lambda expressions...

                ToolConfigList.AddItem(new ListBoxItem(res.ToString(), Program.Map.Config.Tools[res].Todo.ToString()));

                var ctrl = ToolConfigList.FindControl("Overlay_" + res);
                var bar = ctrl.Children.First() as Image;
                var label = ToolConfigList.FindControl("Count_" + res) as Label;
                var item = Program.Map.Config.Tools[res];

                ctrl.OnMouseButtonDown += (sender, x, y, btn) =>
                {
                    Program.Map.ChangeToolSetting(res, item.Todo, x / (double)ctrl.Width);
                };

                label.Text = item.Todo.ToString();
                bar.Width = (int)(ctrl.Width * item.Percentage);
                bar.ProcessScaling();

                (ToolConfigList.FindControl("Up_" + res) as Button).OnClick += (sender) =>
                {
                    Program.Map.ChangeToolSetting(res, item.Todo + 1, item.Percentage);
                };
                (ToolConfigList.FindControl("Down_" + res) as Button).OnClick += (sender) =>
                {
                    Program.Map.ChangeToolSetting(res, item.Todo - 1, item.Percentage);
                };
            }
        }

        public T FindControl<T>(String inName) where T : Control
        {
            Control result;

            if ((result = RootElement.FindControl(inName)) == null)
                throw new ArgumentException("Config does not contain a control named \"" + inName + "\".");

            if(!(result is T))
                throw new ArgumentException("Control named \"" + inName + "\" is not of expected type \"" + typeof(T).FullName + "\".");

            return (T)result;
        }

        public void ShowObjectInspector(RenderableVisual inObjectOrNull)
        {
            bool hasInspector = false;

            if (inObjectOrNull != null)
            {
                if (inObjectOrNull.UserContext is VisualBuilding)
                {
                    BuildingInspector.Show(this, ((VisualBuilding)inObjectOrNull.UserContext).Building, null);
                    hasInspector = true;
                }
            }

            if (!hasInspector)
            {
                // check for build task
                VisualBuildTask task;

                if ((task = VisualBuildTask.GetBuildTaskAt(Program.TerrainRenderer.GridXY)) != null)
                {
                    BuildingInspector.Show(this, task.Task.Building, task.Task);
                    hasInspector = true;
                }
            }

            if (!hasInspector)
            {
                MainMenu.IsVisible = true;
                ObjectInspector.IsVisible = false;

                BuildingInspector.Close();
            }
            else
            {
                MainMenu.IsVisible = false;
                ObjectInspector.IsVisible = true;
            }
        }

        public void UpdateObjectInspector()
        {
            if(BuildingInspector.Instance != null)
                BuildingInspector.Instance.Update();
        }
    }
}
